﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using log4net;
using SyncLib;
using System.IO;
using System.Reflection;
using SyncLib.Interface;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using SyncLib.Data;
using System.Data;
using System.Data.SqlClient;

namespace SyncWCFServiceLibrary
{
    public class SyncManager : RelationalSyncContract, ISyncManager, IDisposable
    {
        ILog Log = LogManager.GetLogger(typeof(Log4NetImplement));
        #region ISyncManager Members

        public void Initialize(SyncLib.Data.ScopeInfoEx scope, SyncProviderPosition position)
        {
            bool flag = false;
            try
            {

                SqlHelper.ExecuteNonQuery(CommonVariable.SqlConnectionString, "DeleteSyncDataBatchByClientId", new object[] { scope.ClientID });

                if (Log.IsDebugEnabled)
                    Log.DebugFormat("Client({0}) call Initialize with ScopeExId={1} ScopeName={2} Position={3}",
                                ClientId, scope.ScopeExId, scope.ScopeName, position);
                else
                    Log.InfoFormat("Client({0}) call Initialize with ScopeExId={1} ScopeName={2}",
                                    ClientId, scope.ScopeExId, scope.ScopeName);
                Log.Info("Clear exists variable");
                if (KeeperSync != null)
                    KeeperSync.EndSync();
                KeeperSync = null;

                if (batchIdToFileMapper != null)
                    this.batchIdToFileMapper.Clear();
                else
                    batchIdToFileMapper = new Dictionary<string, string>();

                if (!scope.Enable)
                    throw new Exception(string.Format("Scope Name =\"{0}\" has disable to sync==> SKIPP", scope.ScopeName));
                string temp = Assembly.GetExecutingAssembly().Location;
                string assPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "\\bin", scope.ServerAssembly);
                string serverClass = scope.ServerClass.Trim() + "," + Path.GetFileNameWithoutExtension(scope.ServerAssembly.Trim());
                if (File.Exists(assPath))
                {
                    Assembly ass = Assembly.LoadFrom(assPath);
                    Type t = ass.GetType(scope.ServerClass);
                    if (t == null)
                        throw new Exception(
                            string.Format("Can't get type {0} in assembly {1}. Please contact to administrator!",
                            serverClass, scope.ServerAssembly));
                    else
                    {
                        KeeperSync = (IKeeperSync)Activator.CreateInstance(t);
                        KeeperSync.ClientId = ClientId;
                        string message;
                        NextAction action = KeeperSync.Initialize(scope, position, out message);
                        if (action == NextAction.Skip)
                        {
                            throw new Exception(
                                string.Format("Call [Initialize] of {0} return Skip action with message \"{1}\"",
                                                serverClass, message));
                        }
                        action = KeeperSync.PrepareSync(out message);
                        if (action == NextAction.Skip)
                        {
                            throw new Exception(
                                string.Format("Call [PrepareSync] of {0} return Skip action with message \"{1}\"",
                                                serverClass, message));
                        }
                        Log.DebugFormat("Test access Provider if {0}", serverClass);
                        KnowledgeSyncProvider test = KeeperSync.Provider;
                        Log.Debug("=> OK");
                    }
                }
                else
                {
                    Log.InfoFormat("Client({0}) call Initialize but Server assembly aseem not exists(File Name={1})",
                                    ClientId, assPath);
                    throw new FileNotFoundException("", scope.ServerAssembly);
                }

            }
            catch (Exception ex)
            {
                if (!flag)
                    Log.Fatal(String.Format("Client({0}) call Initialize with ScopeId={1} but exception:",
                        ClientId, scope.ScopeExId), ex);
                throw new FaultException<WebSyncFaultException>(new WebSyncFaultException("Initialize Fail:", ex), "DCM");
            }
        }


        //public void UploadDependencyData(string fileName, string remotePeerId, byte[] batchContents)
        //{
        //    //CheckAndCreateBatchingDirectory(remotePeerId);

        //    string filePath = CommonVariable.SyncFolderBatch + "\\" + ClientId.ToString() + "\\" + fileName;
        //    if (File.Exists(filePath))
        //        File.Delete(filePath);
        //    FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
        //    using (fs)
        //    {
        //        fs.Write(batchContents, 0, batchContents.Length);
        //    }
        //}
        public void UploadDependencyData(KDataBatch batch)
        {
            using (SqlConnection conn = new SqlConnection(CommonVariable.SqlConnectionString))
            {
                conn.Open();
                using (SqlTransaction tran = conn.BeginTransaction())
                {
                    KDataBatchFactory.SerializeToSql(batch, conn, tran, ClientId, SyncProviderPosition.Local.ToString());
                    tran.Commit();
                }
            }
        }

        public KDataBatch DownloadDependencyData(string fileName)
        {
            using (SqlConnection conn = new SqlConnection(CommonVariable.SqlConnectionString))
            {
                conn.Open();
                using (SqlTransaction tran = conn.BeginTransaction())
                {
                    return KDataBatchFactory.DeserializeFromSql(fileName, conn, tran, ClientId, SyncProviderPosition.Remote.ToString());                    
                }
            }
        }
        public string[] DownloadDependencyData2(DataTable master, ScopeInfoEx info)
        {
            return null;
        }

        public bool WaitOne(Guid clientId, int timeout)
        {
            try
            {
                Log.DebugFormat("Client({0}) try call waitone operator. Current ClientId={1}", ClientId, AppLock.CurrentClient);
                if (AppLock.SetClient(ClientId, timeout))
                {
                    isRelease = false;
                    return true;
                }
                else
                {
                    isRelease = true;
                    return false;
                }


            }
            catch (Exception ex)
            {
                Log.Fatal(string.Format("Client({0}) call WaitOne operator but exception.", ClientId), ex);
                throw new FaultException<WebSyncFaultException>(new WebSyncFaultException("", ex));
            }
        }
        bool isRelease = false;
        public void Release(Guid clientId)
        {
            try
            {
                Guid temp = AppLock.CurrentClient;
                if (temp.Equals(ClientId))
                {
                    Log.DebugFormat("Client({0}) try call Release operator. Current ClientId={1}", ClientId, AppLock.CurrentClient);
                    AppLock.ResetClient(ClientId);
                    isRelease = true;
                }
                else
                    Log.Info(string.Format("Client({0}) cal Release operator but Current ClientId={1} not match", ClientId, AppLock.CurrentClient));

            }
            catch (Exception ex)
            {
                Log.Fatal(string.Format("Client({0}) call Release operator but exception.", ClientId), ex);
                throw new FaultException<WebSyncFaultException>(new WebSyncFaultException("", ex));
            }
        }

        public bool Authentication(Guid clientId, Byte[] securityToken, string userName, string password)
        {
            try
            {
                Log.InfoFormat("Client \"{0}\" Authentication with userName=\"{1}\"", clientId, userName);
                if (ClientId.Equals(clientId))
                    return true;

                string sql = "Select ConfigName,ConfigValue from ServerConfig where ConfigName='UserName' OR ConfigName='Password'";
                DataTable tblData = SqlHelper.ExecuteDataTable(CommonVariable.SqlConnectionString, CommandType.Text, sql);
                if (tblData.Rows.Count == 0)
                    return true;
                if (tblData.Rows.Count == 2)
                {
                    string configUserName = "", configPassword = "";
                    if (tblData.Rows[0]["ConfigName"].ToString().Equals("UserName", StringComparison.InvariantCultureIgnoreCase))
                    {
                        configUserName = tblData.Rows[0]["ConfigValue"].ToString();
                        configPassword = tblData.Rows[1]["ConfigValue"].ToString();
                    }
                    else
                    {
                        configUserName = tblData.Rows[1]["ConfigValue"].ToString();
                        configPassword = tblData.Rows[0]["ConfigValue"].ToString();
                    }
                    if (configUserName.Equals(userName, StringComparison.InvariantCultureIgnoreCase) && configPassword.Equals(password, StringComparison.InvariantCultureIgnoreCase))
                    {
                        ClientId = clientId;
                        Log.InfoFormat("Authentication success :{0}", ClientId);
                        return true;
                    }

                }



            }
            catch (Exception ex)
            {
                Log.Fatal(string.Format("Client({0}) call Authentication operator but exception.", ClientId), ex);
                throw new FaultException<WebSyncFaultException>(new WebSyncFaultException("", ex));
            }
            ClientId = Guid.Empty;
            return false;
        }
        public KSyncStatistics GetSyncStatistics()
        {
            try
            {
                return KeeperSync.SyncStatistics;
            }
            catch (Exception ex)
            {
                Log.Fatal(string.Format("Client({0}) call GetSyncStatistics operator but exception.", ClientId), ex);
                throw new FaultException<WebSyncFaultException>(new WebSyncFaultException("", ex));
            }
        }
        public void KeepAlive()
        {
            Log.Debug(ClientId.ToString() + " KeepAlive");
            AppLock.KeepAlive(ClientId);
        }
        public void ClearSession()
        {
            try
            {
                this.KeeperSync.EndSync();

                SqlHelper.ExecuteNonQuery(CommonVariable.SqlConnectionString, "DeleteSyncDataBatchByClientId", new object[] { KeeperSync.Context.CurrentScope.ClientID });
            }
            catch (Exception ex)
            {
                Log.Fatal(string.Format("Client({0}) call ClearSession operator but exception.", ClientId), ex);
                throw new FaultException<WebSyncFaultException>(new WebSyncFaultException("", ex));

            }

        }

        #endregion

        ~SyncManager()
        {
            Log.Info("**************************************************");
            Log.Info("**************************************************");
            Log.Info("*************WCF CALL DECONSTRUCTOR**************");
            Log.Info("**************************************************");
            Log.Info("**************************************************");
            if (KeeperSync != null)
                KeeperSync.EndSync();
            KeeperSync = null;

        }

        #region IDisposable Members

        public void Dispose()
        {
            Log.Info("**************************************************");
            Log.Info("**************************************************");
            Log.Info("*************WCF CALL Dispose**************");
            Log.Info("**************************************************");
            Log.Info("**************************************************");
            if (KeeperSync != null)
                KeeperSync.EndSync();

            KeeperSync = null;
            if (!isRelease)
            {
                AppLock.ResetClient(ClientId);
            }
            ClientId = Guid.Empty;

        }

        #endregion
        public DataSet GetClientList()
        {
           return SqlHelper.ExecuteDataset(CommonVariable.SqlConnectionString, CommandType.Text, "Select * from Client");
        }
        public void UpdateSyncTimes(Guid clientId, ScopeInfoEx scope)
        {
            string sql="";
            try
            {
                 sql= "Update ClientConfig Set SyncTimes=ISNULL(SyncTimes,0)+1 WHERE ClientId='{0}' AND ScopeExId ='{1}'";
                sql = string.Format(sql, clientId, scope.ScopeExId);
                SqlHelper.ExecuteNonQuery(CommonVariable.SqlConnectionString, CommandType.Text, sql);
            }
            catch(Exception e)
            {
                Log.Fatal("UpdateSyncTimes: " + sql, e);
            }
        }
    }
}
